home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Text / Edit / ElVIs / src / ex.c < prev    next >
C/C++ Source or Header  |  1992-04-07  |  16KB  |  723 lines

  1. /* ex.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the code for reading ex commands. */
  12.  
  13. #include "config.h"
  14. #include "ctype.h"
  15. #include "vi.h"
  16.  
  17. /* This data type is used to describe the possible argument combinations */
  18. typedef short ARGT;
  19. #define FROM    1        /* allow a linespec */
  20. #define    TO    2        /* allow a second linespec */
  21. #define BANG    4        /* allow a ! after the command name */
  22. #define EXTRA    8        /* allow extra args after command name */
  23. #define XFILE    16        /* expand wildcards in extra part */
  24. #define NOSPC    32        /* no spaces allowed in the extra part */
  25. #define    DFLALL    64        /* default file range is 1,$ */
  26. #define DFLNONE    128        /* no default file range */
  27. #define NODFL    256        /* do not default to the current file name */
  28. #define EXRCOK    512        /* can be in a .exrc file */
  29. #define NL    1024        /* if mode!=MODE_EX, then write a newline first */
  30. #define PLUS    2048        /* allow a line number, as in ":e +32 foo" */
  31. #define ZERO    4096        /* allow 0 to be given as a line number */
  32. #define NOBAR    8192        /* treat following '|' chars as normal */
  33. #define FILES    (XFILE + EXTRA)    /* multiple extra files allowed */
  34. #define WORD1    (EXTRA + NOSPC)    /* one extra word allowed */
  35. #define FILE1    (FILES + NOSPC)    /* 1 file allowed, defaults to current file */
  36. #define NAMEDF    (FILE1 + NODFL)    /* 1 file allowed, defaults to "" */
  37. #define NAMEDFS    (FILES + NODFL)    /* multiple files allowed, default is "" */
  38. #define RANGE    (FROM + TO)    /* range of linespecs allowed */
  39. #define NONE    0        /* no args allowed at all */
  40.  
  41. /* This array maps ex command names to command codes. The order in which
  42.  * command names are listed below is significant -- ambiguous abbreviations
  43.  * are always resolved to be the first possible match.  (e.g. "r" is taken
  44.  * to mean "read", not "rewind", because "read" comes before "rewind")
  45.  */
  46. static struct
  47. {
  48.     char    *name;    /* name of the command */
  49.     CMD    code;    /* enum code of the command */
  50.     void    (*fn)();/* function which executes the command */
  51.     ARGT    argt;    /* command line arguments permitted/needed/used */
  52. }
  53.     cmdnames[] =
  54. {   /*    cmd name    cmd code    function    arguments */
  55.     {"append",    CMD_APPEND,    cmd_append,    FROM+ZERO+BANG    },
  56. #ifdef DEBUG
  57.     {"bug",        CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA+NL},
  58. #endif
  59.     {"change",    CMD_CHANGE,    cmd_append,    RANGE+BANG    },
  60.     {"delete",    CMD_DELETE,    cmd_delete,    RANGE+WORD1    },
  61.     {"edit",    CMD_EDIT,    cmd_edit,    BANG+FILE1+PLUS    },
  62.     {"file",    CMD_FILE,    cmd_file,    NAMEDF        },
  63.     {"global",    CMD_GLOBAL,    cmd_global,    RANGE+BANG+EXTRA+DFLALL+NOBAR},
  64.     {"insert",    CMD_INSERT,    cmd_append,    FROM+BANG    },
  65.     {"join",    CMD_INSERT,    cmd_join,    RANGE+BANG    },
  66.     {"k",        CMD_MARK,    cmd_mark,    FROM+WORD1    },
  67.     {"list",    CMD_LIST,    cmd_print,    RANGE+NL    },
  68.     {"move",    CMD_MOVE,    cmd_move,    RANGE+EXTRA    },
  69.     {"next",    CMD_NEXT,    cmd_next,    BANG+NAMEDFS    },
  70.     {"Next",    CMD_PREVIOUS,    cmd_next,    BANG        },
  71.     {"print",    CMD_PRINT,    cmd_print,    RANGE+NL    },
  72.     {"quit",    CMD_QUIT,    cmd_xit,    BANG        },
  73.     {"read",    CMD_READ,    cmd_read,    FROM+ZERO+NAMEDF},
  74.     {"substitute",    CMD_SUBSTITUTE,    cmd_substitute,    RANGE+EXTRA    },
  75.     {"to",        CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  76.     {"undo",    CMD_UNDO,    cmd_undo,    NONE        },
  77.     {"vglobal",    CMD_VGLOBAL,    cmd_global,    RANGE+EXTRA+DFLALL+NOBAR},
  78.     {"write",    CMD_WRITE,    cmd_write,    RANGE+BANG+FILE1+DFLALL},
  79.     {"xit",        CMD_XIT,    cmd_xit,    BANG+NL        },
  80.     {"yank",    CMD_YANK,    cmd_delete,    RANGE+WORD1    },
  81.  
  82.     {"!",        CMD_BANG,    cmd_shell,    EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR},
  83.     {"#",        CMD_NUMBER,    cmd_print,    RANGE+NL    },
  84.     {"<",        CMD_SHIFTL,    cmd_shift,    RANGE        },
  85.     {">",        CMD_SHIFTR,    cmd_shift,    RANGE        },
  86.     {"=",        CMD_EQUAL,    cmd_file,    RANGE        },
  87.     {"&",        CMD_SUBAGAIN,    cmd_substitute,    RANGE        },
  88. #ifndef NO_AT
  89.     {"@",        CMD_AT,        cmd_at,        EXTRA        },
  90. #endif
  91.  
  92. #ifndef NO_ABBR
  93.     {"abbreviate",    CMD_ABBR,    cmd_map,    EXRCOK+BANG+EXTRA},
  94. #endif
  95.     {"args",    CMD_ARGS,    cmd_args,    EXRCOK+NAMEDFS    },
  96. #ifndef NO_ERRLIST
  97.     {"cc",        CMD_CC,        cmd_make,    BANG+FILES    },
  98. #endif
  99.     {"cd",        CMD_CD,        cmd_cd,        EXRCOK+BANG+NAMEDF},
  100.     {"copy",    CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  101. #ifndef NO_DIGRAPH
  102.     {"digraph",    CMD_DIGRAPH,    cmd_digraph,    EXRCOK+BANG+EXTRA},
  103. #endif
  104. #ifndef NO_ERRLIST
  105.     {"errlist",    CMD_ERRLIST,    cmd_errlist,    BANG+NAMEDF    },
  106. #endif
  107.     {"ex",        CMD_EDIT,    cmd_edit,    BANG+FILE1    },
  108.     {"mark",    CMD_MARK,    cmd_mark,    FROM+WORD1    },
  109. #ifndef NO_MKEXRC
  110.     {"mkexrc",    CMD_MKEXRC,    cmd_mkexrc,    NAMEDF        },
  111. #endif
  112.     {"number",    CMD_NUMBER,    cmd_print,    RANGE+NL    },
  113.     {"put",        CMD_PUT,    cmd_put,    FROM+ZERO+WORD1    },
  114.     {"set",        CMD_SET,    cmd_set,    EXRCOK+EXTRA    },
  115.     {"shell",    CMD_SHELL,    cmd_shell,    NL        },
  116.     {"source",    CMD_SOURCE,    cmd_source,    EXRCOK+NAMEDF    },
  117. #ifdef SIGTSTP
  118.     {"stop",    CMD_STOP,    cmd_suspend,    NONE        },
  119. #endif
  120.     {"tag",        CMD_TAG,    cmd_tag,    BANG+WORD1    },
  121.     {"version",    CMD_VERSION,    cmd_version,    EXRCOK+NONE    },
  122.     {"visual",    CMD_VISUAL,    cmd_edit,    BANG+NAMEDF    },
  123.     {"wq",        CMD_WQUIT,    cmd_xit,    NL        },
  124.  
  125. #ifdef DEBUG
  126.     {"debug",    CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA+NL},
  127.     {"validate",    CMD_VALIDATE,    cmd_validate,    BANG+NL        },
  128. #endif
  129.     {"chdir",    CMD_CD,        cmd_cd,        EXRCOK+BANG+NAMEDF},
  130. #ifndef NO_COLOR
  131.     {"color",    CMD_COLOR,    cmd_color,    EXRCOK+EXTRA    },
  132. #endif
  133. #ifndef NO_ERRLIST
  134.     {"make",    CMD_MAKE,    cmd_make,    BANG+NAMEDFS    },
  135. #endif
  136.     {"map",        CMD_MAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  137.     {"previous",    CMD_PREVIOUS,    cmd_next,    BANG        },
  138.     {"rewind",    CMD_REWIND,    cmd_next,    BANG        },
  139. #ifdef SIGTSTP
  140.     {"suspend",    CMD_SUSPEND,    cmd_suspend,    NONE        },
  141. #endif
  142.     {"unmap",    CMD_UNMAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  143. #ifndef NO_ABBR
  144.     {"unabbreviate",CMD_UNABBR,    cmd_map,    EXRCOK+WORD1    },
  145. #endif
  146.  
  147.     {(char *)0}
  148. };
  149.  
  150.  
  151. /* This function parses a search pattern - given a pointer to a / or ?,
  152.  * it replaces the ending / or ? with a \0, and returns a pointer to the
  153.  * stuff that came after the pattern.
  154.  */
  155. char    *parseptrn(ptrn)
  156.     REG char    *ptrn;
  157. {
  158.     REG char     *scan;
  159.  
  160.     for (scan = ptrn + 1;
  161.          *scan && *scan != *ptrn;
  162.          scan++)
  163.     {
  164.         /* allow backslashed versions of / and ? in the pattern */
  165.         if (*scan == '\\' && scan[1] != '\0')
  166.         {
  167.             scan++;
  168.         }
  169.     }
  170.     if (*scan)
  171.     {
  172.         *scan++ = '\0';
  173.     }
  174.  
  175.     return scan;
  176. }
  177.  
  178.  
  179. /* This function parses a line specifier for ex commands */
  180. char *linespec(s, markptr)
  181.     REG char    *s;        /* start of the line specifier */
  182.     MARK        *markptr;    /* where to store the mark's value */
  183. {
  184.     long        num;
  185.     REG char    *t;
  186.  
  187.     /* parse each ;-delimited clause of this linespec */
  188.     do
  189.     {
  190.         /* skip an initial ';', if any */
  191.         if (*s == ';')
  192.         {
  193.             s++;
  194.         }
  195.  
  196.         /* skip leading spaces */
  197.         while (isspace(*s))
  198.         {
  199.             s++;
  200.         }
  201.  
  202.         /* dot means current position */
  203.         if (*s == '.')
  204.         {
  205.             s++;
  206.             *markptr = cursor;
  207.         }
  208.         /* '$' means the last line */
  209.         else if (*s == '$')
  210.         {
  211.             s++;
  212.             *markptr = MARK_LAST;
  213.         }
  214.         /* digit means an absolute line number */
  215.         else if (isdigit(*s))
  216.         {
  217.             for (num = 0; isdigit(*s); s++)
  218.             {
  219.                 num = num * 10 + *s - '0';
  220.             }
  221.             *markptr = MARK_AT_LINE(num);
  222.         }
  223.         /* appostrophe means go to a set mark */
  224.         else if (*s == '\'')
  225.         {
  226.             s++;
  227.             *markptr = m_tomark(cursor, 1L, (int)*s);
  228.             s++;
  229.         }
  230.         /* slash means do a search */
  231.         else if (*s == '/' || *s == '?')
  232.         {
  233.             /* put a '\0' at the end of the search pattern */
  234.             t = parseptrn(s);
  235.  
  236.             /* search for the pattern */
  237.             *markptr &= ~(BLKSIZE - 1);
  238.             if (*s == '/')
  239.             {
  240.                 pfetch(markline(*markptr));
  241.                 if (plen > 0)
  242.                     *markptr += plen - 1;
  243.                 *markptr = m_fsrch(*markptr, s);
  244.             }
  245.             else
  246.             {
  247.                 *markptr = m_bsrch(*markptr, s);
  248.             }
  249.  
  250.             /* adjust command string pointer */
  251.             s = t;
  252.         }
  253.  
  254.         /* if linespec was faulty, quit now */
  255.         if (!*markptr)
  256.         {
  257.             return s;
  258.         }
  259.  
  260.         /* maybe add an offset */
  261.         t = s;
  262.         if (*t == '-' || *t == '+')
  263.         {
  264.             s++;
  265.             for (num = 0; isdigit(*s); s++)
  266.             {
  267.                 num = num * 10 + *s - '0';
  268.             }
  269.             if (num == 0)
  270.             {
  271.                 num = 1;
  272.             }
  273.             *markptr = m_updnto(*markptr, num, *t);
  274.         }
  275.     } while (*s == ';' || *s == '+' || *s == '-');
  276.  
  277.     /* protect against invalid line numbers */
  278.     num = markline(*markptr);
  279.     if (num < 1L || num > nlines)
  280.     {
  281.         msg("Invalid line number -- must be from 1 to %ld", nlines);
  282.         *markptr = MARK_UNSET;
  283.     }
  284.  
  285.     return s;
  286. }
  287.  
  288.  
  289.  
  290. /* This function reads an ex command and executes it. */
  291. void ex()
  292. {
  293.     char        cmdbuf[150];
  294.     REG int        cmdlen;
  295.     static long    oldline;
  296.  
  297.     significant = FALSE;
  298.     oldline = markline(cursor);
  299.  
  300.     while (mode == MODE_EX)
  301.     {
  302.         /* read a line */
  303. #ifdef CRUNCH
  304.         cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf));
  305. #else
  306.         cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf));
  307. #endif
  308.         if (cmdlen < 0)
  309.         {
  310.             return;
  311.         }
  312.  
  313.         /* if empty line, assume ".+1" */
  314.         if (cmdlen == 0)
  315.         {
  316.             strcpy(cmdbuf, ".+1");
  317.             qaddch('\r');
  318.             clrtoeol();
  319.         }
  320.         else
  321.         {
  322.             addch('\n');
  323.         }
  324.         refresh();
  325.  
  326.         /* parse & execute the command */
  327.         doexcmd(cmdbuf);
  328.  
  329.         /* handle autoprint */
  330.         if (significant || markline(cursor) != oldline)
  331.         {
  332.             significant = FALSE;
  333.             oldline = markline(cursor);
  334.             if (*o_autoprint && mode == MODE_EX)
  335.             {
  336.                 cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
  337.             }
  338.         }
  339.     }
  340. }
  341.  
  342. void doexcmd(cmdbuf)
  343.     char        *cmdbuf;    /* string containing an ex command */
  344. {
  345.     REG char    *scan;        /* used to scan thru cmdbuf */
  346.     MARK        frommark;    /* first linespec */
  347.     MARK        tomark;        /* second linespec */
  348.     REG int        cmdlen;        /* length of the command name given */
  349.     CMD        cmd;        /* what command is this? */
  350.     ARGT        argt;        /* argument types for this command */
  351.     short        forceit;    /* bang version of a command? */
  352.     REG int        cmdidx;        /* index of command */
  353.     REG char    *build;        /* used while copying filenames */
  354.     int        iswild;        /* boolean: filenames use wildcards? */
  355.     int        isdfl;        /* using default line ranges? */
  356.     int        didsub;        /* did we substitute file names for % or # */
  357.  
  358.     /* ex commands can't be undone via the shift-U command */
  359.     U_line = 0L;
  360.  
  361.     /* permit extra colons at the start of the line */
  362.     for (; *cmdbuf == ':'; cmdbuf++)
  363.     {
  364.     }
  365.  
  366.     /* ignore command lines that start with a double-quote */
  367.     if (*cmdbuf == '"')
  368.     {
  369.         return;
  370.     }
  371.     scan = cmdbuf;
  372.  
  373.     /* parse the line specifier */
  374.     if (nlines < 1)
  375.     {
  376.         /* no file, so don't allow addresses */
  377.     }
  378.     else if (*scan == '%')
  379.     {
  380.         /* '%' means all lines */
  381.         frommark = MARK_FIRST;
  382.         tomark = MARK_LAST;
  383.         scan++;
  384.     }
  385.     else if (*scan == '0')
  386.     {
  387.         frommark = tomark = MARK_UNSET;
  388.         scan++;
  389.     }
  390.     else
  391.     {
  392.         frommark = cursor;
  393.         scan = linespec(scan, &frommark);
  394.         tomark = frommark;
  395.         if (frommark && *scan == ',')
  396.         {
  397.             scan++;
  398.             scan = linespec(scan, &tomark);
  399.         }
  400.         if (!tomark)
  401.         {
  402.             /* faulty line spec -- fault already described */
  403.             return;
  404.         }
  405.         if (frommark > tomark)
  406.         {
  407.             msg("first address exceeds the second");
  408.             return;
  409.         }
  410.     }
  411.     isdfl = (scan == cmdbuf);
  412.  
  413.     /* skip whitespace */
  414.     while (isspace(*scan))
  415.     {
  416.         scan++;
  417.     }
  418.  
  419.     /* if no command, then just move the cursor to the mark */
  420.     if (!*scan)
  421.     {
  422.         if (tomark != MARK_UNSET)
  423.             cursor = tomark;
  424.         return;
  425.     }
  426.  
  427.     /* figure out how long the command name is */
  428.     if (!isalpha(*scan))
  429.     {
  430.         cmdlen = 1;
  431.     }
  432.     else
  433.     {
  434.         for (cmdlen = 1;
  435.              isalpha(scan[cmdlen]);
  436.              cmdlen++)
  437.         {
  438.         }
  439.     }
  440.  
  441.     /* lookup the command code */
  442.     for (cmdidx = 0;
  443.          cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
  444.          cmdidx++)
  445.     {
  446.     }
  447.     argt = cmdnames[cmdidx].argt;
  448.     cmd = cmdnames[cmdidx].code;
  449.     if (cmd == CMD_NULL)
  450.     {
  451.         msg("Unknown command \"%.*s\"", cmdlen, scan);
  452.         return;
  453.     }
  454.  
  455.     /* !!! if the command doesn't have NOBAR set, then replace | with \0 */
  456.  
  457.     /* if the command ended with a bang, set the forceit flag */
  458.     scan += cmdlen;
  459.     if ((argt & BANG) && *scan == '!')
  460.     {
  461.         scan++;
  462.         forceit = 1;
  463.     }
  464.     else
  465.     {
  466.         forceit = 0;
  467.     }
  468.  
  469.     /* skip any more whitespace, to leave scan pointing to arguments */
  470.     while (isspace(*scan))
  471.     {
  472.         scan++;
  473.     }
  474.  
  475.     /* a couple of special cases for filenames */
  476.     if (argt & XFILE)
  477.     {
  478.         /* if names were given, process them */
  479.         if (*scan)
  480.         {
  481.             for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
  482.             {
  483.                 switch (*scan)
  484.                 {
  485.                   case '\\':
  486.                     if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#')
  487.                     {
  488.                         *build++ = *++scan;
  489.                     }
  490.                     else
  491.                     {
  492.                         *build++ = '\\';
  493.                     }
  494.                     break;
  495.  
  496.                   case '%':
  497.                     if (!*origname)
  498.                     {
  499.                         msg("No filename to substitute for %%");
  500.                         return;
  501.                     }
  502.                     strcpy(build, origname);
  503.                     while (*build)
  504.                     {
  505.                         build++;
  506.                     }
  507.                     didsub = TRUE;
  508.                     break;
  509.  
  510.                   case '#':
  511.                     if (!*prevorig)
  512.                     {
  513.                         msg("No filename to substitute for #");
  514.                         return;
  515.                     }
  516.                     strcpy(build, prevorig);
  517.                     while (*build)
  518.                     {
  519.                         build++;
  520.                     }
  521.                     didsub = TRUE;
  522.                     break;
  523.  
  524.                   case '*':
  525.                   case '?':
  526. #if !(MSDOS || TOS)
  527.                   case '[':
  528.                   case '`':
  529.                   case '{': /* } */
  530.                   case '$':
  531.                   case '~':
  532. #endif
  533.                     *build++ = *scan;
  534.                     iswild = TRUE;
  535.                     break;
  536.  
  537.                   default:
  538.                     *build++ = *scan;
  539.                 }
  540.             }
  541.             *build = '\0';
  542.  
  543.             if (cmd == CMD_BANG
  544.              || cmd == CMD_READ && tmpblk.c[0] == '!'
  545.              || cmd == CMD_WRITE && tmpblk.c[0] == '!')
  546.             {
  547.                 if (didsub)
  548.                 {
  549.                     if (mode != MODE_EX)
  550.                     {
  551.                         addch('\n');
  552.                     }
  553.                     addstr(tmpblk.c);
  554.                     addch('\n');
  555.                     exrefresh();
  556.                 }
  557.             }
  558.             else
  559.             {
  560.                 if (iswild && tmpblk.c[0] != '>')
  561.                 {
  562.                     scan = wildcard(tmpblk.c);
  563.                 }
  564.             }
  565.         }
  566.         else /* no names given, maybe assume origname */
  567.         {
  568.             if (!(argt & NODFL))
  569.             {
  570.                 strcpy(tmpblk.c, origname);
  571.             }
  572.             else
  573.             {
  574.                 *tmpblk.c = '\0';
  575.             }
  576.         }
  577.  
  578.         scan = tmpblk.c;
  579.     }
  580.  
  581.     /* bad arguments? */
  582.     if (!(argt & EXRCOK) && nlines < 1L)
  583.     {
  584.         msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
  585.         return;
  586.     }
  587.     if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
  588.     {
  589.         msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
  590.         return;
  591.     }
  592.     if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
  593.     {
  594.         msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
  595.         return;
  596.     }
  597.     if (!(argt & TO) && tomark != frommark && nlines >= 1L)
  598.     {
  599.         msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
  600.         return;
  601.     }
  602.     if (!(argt & EXTRA) && *scan)
  603.     {
  604.         msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
  605.         return;
  606.     }
  607.     if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
  608.     {
  609.         build = scan;
  610. #ifndef CRUNCH
  611.         if ((argt & PLUS) && *build == '+')
  612.         {
  613.             while (*build && !isspace(*build))
  614.             {
  615.                 build++;
  616.             }
  617.             while (*build && isspace(*build))
  618.             {
  619.                 build++;
  620.             }
  621.         }
  622. #endif /* not CRUNCH */
  623.         for (; *build; build++)
  624.         {
  625.             if (isspace(*build))
  626.             {
  627.                 msg("Too many %s to \"%s\" command.",
  628.                     (argt & XFILE) ? "filenames" : "arguments",
  629.                     cmdnames[cmdidx].name);
  630.                 return;
  631.             }
  632.         }
  633.     }
  634.  
  635.     /* some commands have special default ranges */
  636.     if (isdfl && (argt & DFLALL))
  637.     {
  638.         frommark = MARK_FIRST;
  639.         tomark = MARK_LAST;
  640.     }
  641.     else if (isdfl && (argt & DFLNONE))
  642.     {
  643.         frommark = tomark = 0L;
  644.     }
  645.  
  646.     /* write a newline if called from visual mode */
  647.     if ((argt & NL) && mode != MODE_EX && !exwrote)
  648.     {
  649.         addch('\n');
  650.         exrefresh();
  651.     }
  652.  
  653.     /* act on the command */
  654.     (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
  655. }
  656.  
  657.  
  658. /* This function executes EX commands from a file.  It returns 1 normally, or
  659.  * 0 if the file could not be opened for reading.
  660.  */
  661. int doexrc(filename)
  662.     char    *filename;    /* name of a ".exrc" file */
  663. {
  664.     int    fd;        /* file descriptor */
  665.     int    len;        /* length of the ".exrc" file */
  666.  
  667.     /* !!! kludge: we use U_text as the buffer.  This has the side-effect
  668.      * of interfering with the shift-U visual command.  Disable shift-U.
  669.      */
  670.     U_line = 0L;
  671.  
  672.     /* open the file, read it, and close */
  673.     fd = open(filename, O_RDONLY);
  674.     if (fd < 0)
  675.     {
  676.         return 0;
  677.     }
  678.     len = tread(fd, U_text, BLKSIZE);
  679.     close(fd);
  680.  
  681.     /* execute the string */
  682.     exstring(U_text, len, ctrl('V'));
  683.  
  684.     return 1;
  685. }
  686.  
  687. /* This function executes EX commands from a string.  The commands may be
  688.  * separated by newlines or by | characters.  It also handles quoting.
  689.  * Each individual command is limited to 132 bytes, but the total string
  690.  * may be longer.
  691.  */
  692. void exstring(buf, len, qchar)
  693.     char    *buf;    /* the commands to execute */
  694.     int    len;    /* the length of the string */
  695.     int    qchar;    /* the quote character -- ^V for file, or \ for kbd */
  696. {
  697.     char    single[133];    /* a single command */
  698.     char    *src, *dest;
  699.     int    i;
  700.  
  701.     /* find & do each command */
  702.     for (src = buf; src < &buf[len]; src++)
  703.     {
  704.         /* Copy a single command into single[].  Convert any quoted |
  705.          * into a normal |, and stop at a newline or unquoted |.
  706.          */
  707.         for (dest = single, i = 0;
  708.              i < 132 && src < &buf[len] && *src != '\n' && *src != '|';
  709.              src++, i++)
  710.         {
  711.             if (src[0] == qchar && src[1] == '|')
  712.             {
  713.                 src++;
  714.             }
  715.             *dest++ = *src;
  716.         }
  717.         *dest = '\0';
  718.  
  719.         /* do it */
  720.         doexcmd(single);
  721.     }
  722. }
  723.